/*
 * PopupUsersManager.cpp
 *
 *  Created on: Jul 7, 2012
 *      Author: guillou
 */
#include <PopupUsersManager.hpp>
#include <PopupSettings.hpp>

PopupUsersManager *PopupUsersManager::m_instance = 0;

using namespace std;
using namespace Popup;

PopupUsersManager *PopupUsersManager::instance()
{
  if (m_instance == 0)
  {
    m_instance = new PopupUsersManager;
  }
  return m_instance;
}

SessionID PopupUsersManager::getSessionID() const {
  SessionID _id = 0;
  m_mutex.lock();
  _id = m_sessionID;
  m_mutex.unlock();
  return _id;
}

UserID PopupUsersManager::getMyID() const {
  UserID _id = 0;
  m_mutex.lock();
  if (m_me != 0) {
    _id = m_me->getID();
  }
  m_mutex.unlock();
  return _id;
}

bool PopupUsersManager::getUserInfo(PopupUserExt & p_user,
                                    UserID p_userID,
                                    unsigned short p_infomask) const
{
  bool _rc = false;
  const PopupUserExt *_user = 0;

  m_mutex.lock();

  // Search for appropriate user
  if (p_userID == 0) {
    _user = m_me;
  } else {
    PopupUserExtMap::const_iterator _it = m_users.find(p_userID);
    if (_it != m_users.end()) {
      _user = &_it->second;
    }
  }

  // Fill output user info
  if (_user != 0) {
    _rc = true;
    p_user.update(*_user, p_infomask);
    if (p_infomask & PopupUserExt::POPUP_USER_FIELD_STATS) {
      p_user.stats = _user->stats;
    }
  }

  m_mutex.unlock();

  return _rc;
}

void PopupUsersManager::getIconFromMode(QIcon & p_icon,
                                        UserMode p_mode) const
{
  switch (p_mode)
  {
    case POPUP_MODE_TEXTONLY:
      p_icon = QIcon(":/images/popup_text.png");
      break;
    case POPUP_MODE_PROMPT:
      p_icon = QIcon(":/images/popup_prompt.png");
      break;
    case POPUP_MODE_MUTED:
      p_icon = QIcon(":/images/popup_quiet.png");
      break;
    case POPUP_MODE_DEFAULT:
    default:
      p_icon = QIcon(":/images/popup_full.png");
      break;
   }
}

string PopupUsersManager::getNickname(UserID p_userID) const {
  PopupUserExt _user;
  if (getUserInfo(_user, p_userID, POPUP_USER_FIELD_NICKNAME)) {
    return _user.getNickname();
  } else {
    return "{!ERR_NICKNAME!}";
  }
}

string PopupUsersManager::getAvatar(UserID p_userID) const {
  PopupUserExt _user;
  if (getUserInfo(_user, p_userID, POPUP_USER_FIELD_AVATAR)) {
    return _user.getAvatar();
  } else {
    return "{!ERR_AVATAR!}";
  }
}

UserMode PopupUsersManager::getMode(UserID p_userID) const {
  PopupUserExt _user;
  if (getUserInfo(_user, p_userID, POPUP_USER_FIELD_MODE)) {
    return _user.getMode();
  } else {
    return POPUP_MODE_DEFAULT;
  }
}

UserMode PopupUsersManager::getNextMode(UserMode p_mode) const {
  UserMode _nextMode;
  switch (p_mode)
  {
    case POPUP_MODE_DEFAULT:
      _nextMode = POPUP_MODE_PROMPT; break;
    case POPUP_MODE_PROMPT:
      _nextMode = POPUP_MODE_TEXTONLY; break;
    case POPUP_MODE_TEXTONLY:
      _nextMode = POPUP_MODE_MUTED; break;
    case POPUP_MODE_MUTED:
    default:
      _nextMode = POPUP_MODE_DEFAULT; break;
  }
  return _nextMode;
}

bool PopupUsersManager::isAlive(UserID p_userID) const {
  PopupUserExt _user;
  if (getUserInfo(_user, p_userID, POPUP_USER_FIELD_HEALTH)) {
    return _user.isAlive();
  } else {
    return false;
  }
}

void PopupUsersManager::setNickname(const std::string & p_nickname)
{
  PopupUserExt _user;
  bool _ok = false;
  m_mutex.lock();
  if (m_me != 0) {
    _user.nickname = p_nickname;
    _user.id = m_me->getID();
    _ok = true;
  }
  m_mutex.unlock();

  if (_ok) {
    emit signal_localUserUpdated(&_user, POPUP_USER_FIELD_NICKNAME);
    set<PopupUsersObserver*>::iterator _itobs;
    for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
      (*_itobs)->localUserUpdated(POPUP_USER_FIELD_NICKNAME);
    }
  }
}

void PopupUsersManager::setAvatar(const std::string & p_avatar)
{
  PopupUserExt _user;
  bool _ok = false;
  m_mutex.lock();
  if (m_me != 0) {
    _user.avatar = p_avatar;
    _user.id = m_me->getID();
    _ok = true;
  }
  m_mutex.unlock();

  if (_ok) {
    emit signal_localUserUpdated(&_user, POPUP_USER_FIELD_AVATAR);
    set<PopupUsersObserver*>::iterator _itobs;
    for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
      (*_itobs)->localUserUpdated(POPUP_USER_FIELD_AVATAR);
    }
  }
}

void PopupUsersManager::setMode(UserMode p_mode) {
  PopupUserExt _user;
  bool _ok = false;
  m_mutex.lock();
  if (m_me != 0) {
    m_me->mode = p_mode;
    _user.mode = p_mode;
    _user.id = m_me->getID();
    _ok = true;
  }
  m_mutex.unlock();

  if (_ok) {
    emit signal_localUserUpdated(&_user, POPUP_USER_FIELD_MODE);
    set<PopupUsersObserver*>::iterator _itobs;
    for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
      (*_itobs)->localUserUpdated(POPUP_USER_FIELD_MODE);
    }
  }
}

void PopupUsersManager::slot_onConnectionUpdate(bool p_connected,
                                                User p_myself,
                                                SessionID p_sessionID)
{
  set<PopupUsersObserver*>::iterator _itobs;

  m_mutex.lock();

  m_sessionID = p_sessionID;

  if (p_connected == true) {
    m_users[p_myself.getID()] = p_myself;
    m_me = &m_users[p_myself.getID()];
  } else if (m_me != 0) {
    m_me->setAlive(false);
  }

  // Notify all observers
  if (p_connected)
  {
    PopupUserExtMap::iterator _ituser;
    for (_ituser = m_users.begin(); _ituser != m_users.end(); _ituser++) {
      for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
        (*_itobs)->userUpdated(&_ituser->second, POPUP_USER_FIELD_ALL, true);
      }
    }
  }
  else if (m_me != 0)
  {
    for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
      (*_itobs)->userUpdated(m_me, POPUP_USER_FIELD_HEALTH, false);
    }
  }
  m_mutex.unlock();

  if (p_connected == true) {
    setMode(PopupSettings::instance()->defaultClientMode());
  }
}

void PopupUsersManager::slot_updateUser(Popup::User p_user,
                                        unsigned short p_mask,
                                        bool p_isNewUser)
{
  m_mutex.lock();

  PopupUserExtMap::iterator _it = m_users.find(p_user.getID());
  // User not found
  if (_it == m_users.end()) {
    m_users[p_user.getID()] = p_user;
  }
  // Update existing user
  else {
    m_users[p_user.getID()].update(p_user, p_mask);
  }

  // Notify all observers
  PopupUserExt *_user = &m_users[p_user.getID()];
  set<PopupUsersObserver*>::iterator _itobs;
  for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
    (*_itobs)->userUpdated(_user, p_mask, p_isNewUser);
  }
  m_mutex.unlock();
}

void PopupUsersManager::slot_updateUserStatistics(Popup::UserID p_userID,
                                                  Popup::UserStatistics p_stats)
{
  unsigned short _updateMask = p_stats.itemsMask();
  static const int MAX_NB_ITEMS = 4;
  static const UserRateItem _allItems[MAX_NB_ITEMS] = {
      POPUP_RATE_LIKE, POPUP_RATE_DONTLIKE,
      POPUP_RATE_POETRY, POPUP_RATE_FUCKYOU
  };

  m_mutex.lock();

  PopupUserExtMap::iterator _it = m_users.find(p_userID);
  // User found
  if (_it != m_users.end()) {

    PopupUserExt *_user = &_it->second;

    // Update his stats
    for (int _i = 0; _i <  MAX_NB_ITEMS; _i++) {
      UserRateItem _item = _allItems[_i];
      if (_updateMask & _item) {
        unsigned int _statvalue = 0;
        bool _isRecord = false;
        if (p_stats.get(_item, _statvalue, _isRecord)) {
          _user->stats.set(_item, _statvalue, _isRecord);
        }
      }
    }

    // Notify all observers
    set<PopupUsersObserver*>::iterator _itobs;
    for (_itobs = m_observers.begin(); _itobs != m_observers.end(); _itobs++) {
      (*_itobs)->userStatisticsUpdated(_user, _updateMask);
    }
  }
  m_mutex.unlock();
}

void PopupUsersManager::registerObserver(PopupUsersObserver *p_observer)
{
  m_mutex.lock();
  m_observers.insert(p_observer);
  PopupUserExtMap::iterator _it;
  for (_it = m_users.begin(); _it != m_users.end(); _it++) {
    p_observer->userUpdated(&_it->second, POPUP_USER_FIELD_ALL, true);
    p_observer->userStatisticsUpdated(&_it->second,
                                      _it->second.stats.itemsMask());
  }
  m_mutex.unlock();
}

void PopupUsersManager::unregisterObserver(PopupUsersObserver *p_observer)
{
  m_mutex.lock();
  set<PopupUsersObserver*>::iterator _it = m_observers.find(p_observer);
  if (_it != m_observers.end()) {
    m_observers.erase(_it);
  }
  m_mutex.unlock();
}
